package com.handle.http;

import com.annotations.web.Controller;
import com.annotations.web.RequestBody;
import com.annotations.web.RequestMapping;
import com.annotations.web.RequestParam;
import com.constant.RequestMethod;
import com.protocol.Serializer;
import com.utils.ClassScan;
import com.utils.MyReflectUtils;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.multipart.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

import static com.constant.ServiceConstant.SCAN_PATH;

/**
 * @author linyu.dai
 * @project rpc
 * @description 请求路径处理
 * @creat 2022/11/29 19:56:08
 */
@Slf4j
public class HttpRequestHandle extends HttpBaseHandle {

    //k-->url v-处理对象
    public static final Map<String, HandleMethod> URL_METHOD = new HashMap<>();


    /**
     * @return io.netty.handler.codec.http.DefaultFullHttpResponse
     * @Author linyu.dai
     * @Description 处理请求方法
     * @Date 2022/11/30 10:44
     * @Param [request, paramMap]
     */
    public static DefaultFullHttpResponse process(FullHttpRequest request, Map<String, String> paramMap) {
        try {

            //拿到处理方法 去除参数连接
            HandleMethod handleMethod = URL_METHOD.get(
                    request.uri().substring(0, Optional
                            .of(request.uri().indexOf('?'))
                            .filter(integer -> integer != -1)
                            .orElse(request.uri().length())
                    ));
            if (Objects.isNull(handleMethod)) {
                return error404();
            }

            //判断处理类型
            Method method = handleMethod.getMethod();
            Object object = handleMethod.getObject();

            RequestMapping annotation = method.getAnnotation(RequestMapping.class);
            RequestMethod[] method1 = annotation.method();
            //过滤出符合的类型
            List<RequestMethod> collect =
                    Arrays.stream(method1)
                            .distinct()
                            .filter(requestMethod -> requestMethod.name().equals(request.method().name()))
                            .collect(Collectors.toList());
            if (collect.isEmpty()) {
                //请求错误
                return error400();
            }

            Object[] args;
            //处理文件上传类型
            if (Objects.nonNull(request.headers().get("Content-Type")) && request.headers().get("Content-Type").startsWith("multipart/form-data")) {
                args = getParams(request, method);
            }else{
                args = getParams(method, paramMap);
            }
            //获取参数数组
            getParams(method, paramMap);
            log.info("参数信息{}", args);
            //process .... 对于处理结果 目前只返回json
            Object invoke = method.invoke(object, args);
            return success(Serializer.Algorithm.JSON.serializer(invoke), "application/json");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("处理请求异常....{}", e.getMessage());
            return error500();
        }
    }

    /**
     * @return java.lang.Object[]
     * @Author linyu.dai
     * @Description 处理文件类型
     * @Date 2022/11/30 20:08
     * @Param []
     */
    private static Object[] getParams(FullHttpRequest request, Method method) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        HttpDataFactory factory = new DefaultHttpDataFactory(true);
        HttpPostRequestDecoder httpDecoder = new HttpPostRequestDecoder(factory, request);
        httpDecoder.setDiscardThreshold(0);

        final HttpContent chunk = request;
        httpDecoder.offer(chunk);

        Parameter[] parameters = method.getParameters();
        Object[] objects = new Object[parameters.length];

        if (chunk instanceof LastHttpContent) {
            List<InterfaceHttpData> interfaceHttpDataList = httpDecoder.getBodyHttpDatas();

            //收集数据
            Map<String, List<Object>> map = new HashMap<>();
            for (InterfaceHttpData data : interfaceHttpDataList) {
                List<Object> objs = map.get(data.getName());
                if (objs == null) {
                    ArrayList<Object> objects1 = new ArrayList<>();
                    objects1.add(data);
                    map.put(data.getName(), objects1);
                } else {
                    objs.add(data);
                }
                log.info("file info {}", data);
            }

            //多文件上传
            if (parameters.length == 1 && !MyReflectUtils.isBasics(parameters[0].getType())) {
                //获取所有变量
                Class<?> type = parameters[0].getType();
                Object o = type.getDeclaredConstructor().newInstance();

                log.info("传递过来的参数: {}", map);
                map.forEach((k, v) -> {
                    try {
                        Field field = type.getDeclaredField(k);
                        field.setAccessible(true);
                        if (field.getType().isArray()) {
                            Object[] objects1 = v.toArray();
                            FileUpload[] fileUploads = new FileUpload[objects1.length];
                            for (int i = 0; i < objects1.length; i++) {
                                fileUploads[i] = (FileUpload) objects1[i];
                            }
                            field.set(o, fileUploads);
                        }else{
                            DiskAttribute diskAttribute = (DiskAttribute) v.get(0);
                            Object t = MyReflectUtils.castToWrapperClassObject(field.getType(), diskAttribute.getValue());
                            field.set(o, t);
                        }
                    } catch (Exception e) {
                        log.error("设置值失败。。。{}", e);
                        throw new RuntimeException(e);
                    }
                });
                objects[0] = o;
            } else {
                //单文件上传
                for (int i = 0; i < parameters.length; i++) {
                    if (parameters[i].isAnnotationPresent(RequestParam.class)) {
                        String value = parameters[i].getAnnotation(RequestParam.class).value();
                        List<Object> os = map.get(value);
                        //不支持多文件
                        Object o = os.get(0);
                        if(o instanceof DiskAttribute){
                            DiskAttribute diskAttribute = (DiskAttribute) os.get(0);
                            Object t = MyReflectUtils.castToWrapperClassObject(parameters[i].getType(),diskAttribute.getValue());
                            objects[i] = t;
                        }else{
                            objects[i] = o;
                        }
                    }
                }
            }
        }
        return objects;
    }

    /**
     * @return java.lang.Object[]
     * @Author linyu.dai
     * @Description 获取参数
     * @Date 2022/11/30 19:31
     * @Param [method, paramMap]
     */
    private static Object[] getParams(Method method, Map<String, String> paramMap) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取所有参数
        Parameter[] parameters = method.getParameters();
        Object[] objects = new Object[parameters.length];
        //如果只有一个参数
        if (parameters.length == 1 && !MyReflectUtils.isBasics(parameters[0].getType())) {
            //存在是json
            if (parameters[0].isAnnotationPresent(RequestBody.class)) {
                Class<?> type = parameters[0].getType();
                String value = paramMap.get("jsonData");
                objects[0] = Serializer.objectMapper.readValue(value, type);
            } else {
                //直接替换掉
                String classPath = parameters[0].getParameterizedType().getTypeName();
                Class<?> aClass = Class.forName(classPath);
                //获取实例对象
                Object o = aClass.getDeclaredConstructor().newInstance();
                //获取所有字段
                Arrays.stream(aClass.getDeclaredFields()).forEach(field -> {
                    log.info("field {}", field.getName());
                    //属性值
                    String value = paramMap.get(field.getName());
                    //设置null值
//                        if (Objects.isNull(value)) {
//                            throw new RuntimeException("内容无法匹配解析");
//                        }
                    field.setAccessible(true);
                    //强制类型转换 获取要准换class类型 用class类型方法进行转换
                    Object o1 = MyReflectUtils.castToWrapperClassObject(field.getType(), value);
                    try {
                        field.set(o, o1);
                    } catch (IllegalAccessException e) {
                        log.error("设置值失败{}", e);
                    }
                });
                objects[0] = o;
                return objects;
            }
        }

        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].isAnnotationPresent(RequestParam.class)) {
                String value = parameters[i].getAnnotation(RequestParam.class).value();
                String s = paramMap.get(value);
                Class<?> type = parameters[i].getType();
                Object o1 = MyReflectUtils.castToWrapperClassObject(type, s);
                objects[i] = o1;
            }
        }
        return objects;
    }


    //扫描获取类对应关系
    static {
        ClassScan.scan(SCAN_PATH).stream().filter(path -> Objects.nonNull(ClassScan.getAnnotation(path, Controller.class)))
                .forEach(path -> {
                    try {
                        Class<?> aClass = Class.forName(path);
                        //处理对象
                        Object objectHandle = aClass.getDeclaredConstructor().newInstance();

                        //获取类上的注解信息
                        Controller annotation = aClass.getAnnotation(Controller.class);

                        //获取元路径信息
                        String baseUrl = annotation.value();

                        //获取所有的方法
                        Method[] methods = aClass.getMethods();

                        //遍历
                        for (Method method:methods){
                            //获取方法上的注解
                            if(method.isAnnotationPresent(RequestMapping.class)){
                                RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                                String url = methodAnnotation.url();
                                HandleMethod handleMethod = new HandleMethod(objectHandle, method);
                                String key = baseUrl+url;
                                if(Objects.nonNull(URL_METHOD.putIfAbsent(key, handleMethod))){
                                    throw new RuntimeException("不能具有相同url的方法处理");
                                };
                            }
                        }
                    } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
                             IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException("类加载失败");
                    }
                });
    }

}
